#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   networkEvolutionRulesAnalysis.py
@Contact :   zhujuxing@foxmail.com
@License :   (C)Copyright 2021-2022

@Modify Time      @Author    @Version    @Description
------------      -------    --------    -----------
2021/7/9 12:53   zhujuxing      1.0         None
"""

import functools
from random import choice, random

import pandas as pd

from cloudVirtrualizedNetworkApplicationReliability.src.networkEvolutionConditionGenerate import time_format
from cloudVirtrualizedNetworkApplicationReliability.src.networkEvolutionObjectModel import ControlPlaneType


def network_evolution_rules_analysis_for_evolution_condition(network, evolution_condition_input: pd.DataFrame or str,
                                                             print_info=True):
    g_t = network
    if type(evolution_condition_input) == str:
        try:
            evolution_condition = pd.read_excel(evolution_condition_input)
            evolution_condition['EvolTime'] = evolution_condition['EvolTime'].apply(eval)
            evolution_condition['EvolFailComponetsSet'] = evolution_condition['EvolFailComponetsSet'].apply(eval)
            evolution_condition['EvolRecoComponetsSet'] = evolution_condition['EvolRecoComponetsSet'].apply(eval)
        except Exception:
            raise Exception("输入文件格式错误")
    elif type(evolution_condition_input) == pd.DataFrame:
        evolution_condition = evolution_condition_input
    else:
        raise Exception("输入的演化条件应为Dataframe类型或者响应的.xlsx文件")

    vlinks_state_changed_previous = {}
    vnfs_state_changed_previous = {}
    apps_state_changed_by_previous_vlink = {}
    apps_state_changed_by_previous_vnf = {}
    wait_queue_dict = {}
    fail_detect_previous = {}
    for evol_each_time in evolution_condition.iterrows():
        x = evol_each_time[1]
        if print_info:
            print("\n演化时间为[%20s, %20s]时，%10s故障，%10s修复" % (
                x["EvolTime"][0], x["EvolTime"][1], x["EvolFailComponentsSet"], x["EvolRecoComponentsSet"]))

        # g_t = network_evolution_rules_anlysis_for_single_evolution_state(g_t, x, vlinks_state_changed_previous,
        #                                                                  vnfs_state_changed_previous,
        #                                                                  apps_state_changed_by_previous_vlink,
        #                                                                  apps_state_changed_by_previous_vnf,
        #                                                                  wait_queue_dict, fail_detect_previous,
        #                                                                  print_info)

        try:
            g_t = network_evolution_rules_anlysis_for_single_evolution_state(g_t, x, vlinks_state_changed_previous,
                                                                             vnfs_state_changed_previous,
                                                                             apps_state_changed_by_previous_vlink,
                                                                             apps_state_changed_by_previous_vnf,
                                                                             wait_queue_dict, fail_detect_previous,
                                                                             print_info)
        except Exception as e:
            print("出现异常:%s" % e)
            print("出现异常:%s" % str(e))
            print("演化态出现故障，此时故障节点为%s,修复节点为%s\n" % (x["EvolFailComponentsSet"],
                                                   x["EvolRecoComponentsSet"]) +
                  "已经故障的vlink为%s,已经故障的vnf为%s,vnf影响的业务集合%s,vlink影响的业务集合%s,等待队列为%s, " % (
                      vlinks_state_changed_previous, vnfs_state_changed_previous, apps_state_changed_by_previous_vnf,
                      apps_state_changed_by_previous_vlink, wait_queue_dict))

    # 计算可用度函数
    total_time = evolution_condition.loc[evolution_condition.shape[0] - 1, "EvolTime"][1]
    g_t.application_info["Availability"] = g_t.application_info["WeightedUnavailTime"].apply(
        lambda y: 1 - y / total_time)
    return g_t


def network_evolution_rules_anlysis_for_single_evolution_state(network, x, vlinks_state_changed_previous,
                                                               vnfs_state_changed_previous,
                                                               apps_state_changed_by_previous_vlink,
                                                               apps_state_changed_by_previous_vnf,
                                                               wait_queue_dict, fail_detect_dict_previous,
                                                               print_info):
    result_dict = update_component_state_by_evol(x, network, vnfs_state_changed_previous, vlinks_state_changed_previous,
                                                 apps_state_changed_by_previous_vnf, fail_detect_dict_previous,
                                                 wait_queue_dict, print_info)
    vnfs_state_changed = result_dict["vnfs_state_changed"]
    vlinks_state_changed = result_dict["vlinks_state_changed"]
    fail_detect_dict = result_dict["fail_detect_dict"]
    state = result_dict["state"]
    migration_nil = result_dict["migration_nil"]
    wait_vnf_fail_dict = result_dict["wait_vnf_fail_dict"]
    wait_vnf_fail_nil = result_dict["wait_vnf_fail_nil"]

    if not migration_nil:
        apps_state_changed_by_vlink = update_vlink_state_changed_by_component(vlinks_state_changed, network,
                                                                              fail_detect_dict, state, x,
                                                                              vlinks_state_changed_previous,
                                                                              apps_state_changed_by_previous_vlink,
                                                                              print_info)
        if not wait_vnf_fail_nil:
            update_vnf_state_by_component(vnfs_state_changed, network, fail_detect_dict, state,
                                          apps_state_changed_by_vlink,
                                          x, vnfs_state_changed_previous, vlinks_state_changed_previous,
                                          apps_state_changed_by_previous_vnf, fail_detect_dict_previous,
                                          wait_queue_dict, print_info, up_app=True)
        else:
            update_vnf_state_by_component(vnfs_state_changed, network, fail_detect_dict, state,
                                          apps_state_changed_by_vlink,
                                          x, vnfs_state_changed_previous, vlinks_state_changed_previous,
                                          apps_state_changed_by_previous_vnf, fail_detect_dict_previous,
                                          wait_queue_dict, print_info, up_app=True)

            update_vnf_state_by_component(wait_vnf_fail_dict, network, fail_detect_dict_previous, "fail", {},
                                          x, vnfs_state_changed_previous, vlinks_state_changed_previous,
                                          apps_state_changed_by_previous_vnf, fail_detect_dict_previous,
                                          wait_queue_dict, print_info, up_app=False)
    return network


def update_component_state_by_evol(x, network, vnfs_state_changed_previous, vlink_state_changed_previous,
                                   apps_state_changed_by_previous_vnf, fail_detect_dict_previous,
                                   wait_queue_dict, print_info):
    vnfs_state_changed = {}
    vlinks_state_changed = {}
    fail_detect_dict = {}
    component_info = network.components_info
    migration_nil = False
    wait_vnf_fail_nil = False
    wait_vnf_fail_dict = {}

    if not x['EvolRecoComponentsSet']:
        for fail_component in x['EvolFailComponentsSet']:
            component_info.loc[fail_component, 'State'] = 0
            if component_info.loc[fail_component, 'DataPlane']:
                update_graph_weight(network.edge_info, fail_component, network.data_plane, 'fail')
            if component_info.loc[fail_component, 'ControlPlane']:
                update_graph_weight(network.edge_info, fail_component, network.control_plane, 'fail')

            update_vnf_and_vlink_state_changed(component_info, fail_component, vlinks_state_changed, vnfs_state_changed)

            # 该构件故障是否检测到
            if fail_is_detect(fail_component, network, print_info):
                fail_detect_dict[fail_component] = True
            else:
                fail_detect_dict[fail_component] = False

            # dcgw节点故障引发dcgw控制和转发vnf迁移
            if (component_info.loc[fail_component, 'Type'] == 'DCGW') & \
                    (component_info.loc[fail_component, "DataPlane"]) & \
                    (component_info.loc[fail_component, "Idle"] == 0):
                if fail_detect_dict[fail_component]:
                    nil = migration_dcgw(fail_component, network, x,
                                         vnfs_state_changed_previous,
                                         print_info)
                    if nil:
                        migration_nil = True
                        vlinks_state_changed = {}
                        vnfs_state_changed = {}

            # server引发的vnf迁移
            if (component_info.loc[fail_component, 'Type'] == 'Server') & \
                    (component_info.loc[fail_component, "DataPlane"]) & \
                    (component_info.loc[fail_component, "Idle"] == 0):
                if fail_detect_dict[fail_component]:
                    nil = migration_server(fail_component, network, {}, x,
                                           vnfs_state_changed_previous,
                                           vlink_state_changed_previous,
                                           apps_state_changed_by_previous_vnf,
                                           fail_detect_dict_previous,
                                           wait_queue_dict, print_info)
                    if nil:
                        migration_nil = True
                        vlinks_state_changed = {}
                        vnfs_state_changed = {}
        state_func = "fail"
    else:
        for repair_component in x['EvolRecoComponentsSet']:
            component_info.loc[repair_component, 'State'] = 1

            if component_info.loc[repair_component, 'DataPlane']:
                update_graph_weight(network.edge_info, repair_component, network.data_plane, 'reco')
            if component_info.loc[repair_component, 'ControlPlane']:
                update_graph_weight(network.edge_info, repair_component, network.control_plane, 'reco')

            vnfs_state_changed, vlinks_state_changed = update_vnf_and_vlink_state_changed(component_info,
                                                                                          repair_component,
                                                                                          vlinks_state_changed,
                                                                                          vnfs_state_changed)
            if repair_component in wait_queue_dict.keys():
                vnf_wait_set = wait_queue_dict.pop(repair_component)
                for vnf in vnf_wait_set:
                    vnf_wait_compenent_set = network.vnf_info.loc[vnf, "Wait"]
                    vnf_wait_compenent_set.remove(repair_component)
                    if not vnf_wait_compenent_set:
                        state_dict = network.vnf_info.loc[vnf, "State"]
                        failed_component_list = [k for k in state_dict.keys() if state_dict[k] == 0]
                        for failed_component in failed_component_list:
                            if print_info:
                                print("应该倒换%s的流量了" % failed_component)
                            wait_vnf_fail_dict.update({vnf: {failed_component}})
                            wait_vnf_fail_nil = True
        state_func = "repair"
    result_dict = {"vnfs_state_changed": vnfs_state_changed,
                   "vlinks_state_changed": vlinks_state_changed,
                   "fail_detect_dict": fail_detect_dict,
                   "state": state_func,
                   "migration_nil": migration_nil,
                   "wait_vnf_fail_dict": wait_vnf_fail_dict,
                   "wait_vnf_fail_nil": wait_vnf_fail_nil}
    return result_dict


def component_type(component_name, network):
    return network.components_info.loc[component_name, 'Type']


def fail_is_detect(component_name, network, print_info):
    ct = component_type(component_name, network)
    fail_detect_rate = network.fail_info.loc[network.fail_info.Type == ct].iloc[0]["FDR"]
    if random() < fail_detect_rate:
        if print_info:
            print("%s上的故障已经被检测到" % component_name)
        return True
    else:
        if print_info:
            print("%s上的故障未被检测到" % component_name)
        return False


def migration_dcgw(dcgw, network, x, vnfs_state_changed_previous, print_info):
    components_info = network.components_info
    vnf_info = network.vnf_info
    vlink_info = network.vlink_info
    dcgw_idle_list = components_info.loc[(components_info.Type == "DCGW") & (components_info.Idle == 1)].index.to_list()
    board_list_of_origin_dcgw = board_of_dcgw(dcgw, network)

    vnfs_state_changed = {}
    apps_state_failed_by_vnf = set()

    for board in board_list_of_origin_dcgw:
        vnf = components_info.loc[board, "VNF"]
        if vnf:
            vnf, vnf_backuptype = vnf.copy().pop().split('_')
            if vnf_backuptype == "主":
                if network.vnf_info.loc[vnf, "VNFDataType"] == "DCGW转发":
                    apps_state_failed_by_vnf.union(network.vnf_info.loc[vnf, "ApplicationDeployed"])

    ts = x["EvolTime"][0]
    te = x["EvolTime"][1]

    fdf = network.fail_info
    c_type = components_info.loc[dcgw, "Type"]
    migration_time = time_format(fdf[fdf.Type == c_type]["MST"].to_list()[0])
    migration_rate = fdf[fdf.Type == c_type]["MSR"].to_list()[0]

    for app in network.application_info.index:
        if print_info:
            print("业务%s的流量在时间[%s, %s]小时是%s, 在时间[%s, %s]小时是%s, " % (app, ts, ts + migration_time, 0,
                                                                   ts + migration_time, te, 1))

        weighted_unavail_time = network.application_info.loc[app, "WeightedUnavailTime"]
        network.application_info.loc[app, "WeightedUnavailTime"] = weighted_unavail_time + migration_time

    if bool(dcgw_idle_list) & (random() < migration_rate):
        dcgw_migration = choice(dcgw_idle_list)
        board_list_of_migration_dcgw = board_of_dcgw(dcgw_migration, network)
        # 需要更新：1.components_info里的节点部署信息2.vnf_info里的部署信息3.vlink_info里的信息
        board_migration_dict = dict(zip(board_list_of_origin_dcgw, board_list_of_migration_dcgw))

        for board in board_migration_dict.keys():
            vnf_set = components_info.loc[board, "VNF"]
            if vnf_set:
                vnf_with_backuptype = vnf_set.pop()
                components_info.loc[board_migration_dict[board], "VNF"].add(vnf_with_backuptype)
                vnf, backuptype = vnf_with_backuptype.split("_")
                if backuptype == "主":
                    vnf_info.loc[vnf, "VNFDeployNode"].remove(board)
                    vnf_info.loc[vnf, "VNFDeployNode"].append(board_migration_dict[board])
                    state_of_board = vnf_info.loc[vnf, "State"].pop(board)
                    vnf_info.loc[vnf, "State"].update({board_migration_dict[board]: state_of_board})
                    traffic_of_board = vnf_info.loc[vnf, "Traffic"].pop(board)
                    vnf_info.loc[vnf, "Traffic"].update({board_migration_dict[board]: traffic_of_board})
                elif backuptype == "备":
                    vnf_info.loc[vnf, "VNFBackupNode"].remove(board)
                    vnf_info.loc[vnf, "VNFBackupNode"].append(board_migration_dict[board])
                else:
                    raise (Exception('备份类型错误'))
                vnfs_state_changed[vnf] = set(board_migration_dict[board])

                # vlink
                vlink_of_board_pre = vlink_info.loc[
                    (vlink_info.VlinkSourceVNF == vnf) & (vlink_info.DataType == "数据")].index.to_list()
                for vlink in vlink_of_board_pre:
                    for u, v in list(vlink_info.loc[vlink, "State"].keys()):
                        if u == board:
                            un = board_migration_dict[board]
                            vn = v
                            update_vlink_and_vl_in_component(network, u, un, v, vlink, vlink_info, vn)

                vlink_of_vm_suc = vlink_info.loc[
                    (vlink_info.VlinkDestinationVNF == vnf) & (vlink_info.DataType == "数据")].index.to_list()
                for vlink in vlink_of_vm_suc:
                    for u, v in list(vlink_info.loc[vlink, "State"].keys()):
                        if v == board:
                            un = u
                            vn = board_migration_dict[board]
                            update_vlink_and_vl_in_component(network, u, un, v, vlink, vlink_info, vn)

                if print_info:
                    print("%s_%s从%s迁移到%s上" % (vnf, backuptype, board, board_migration_dict[board]))

            # 迁移完成后，更新vnfs_state_changed_previous里的信息
            for k in vnfs_state_changed_previous.keys():
                v = list(vnfs_state_changed_previous[k])[0]
                if v in board_migration_dict.keys():
                    vnfs_state_changed_previous[k].pop()
                    vnfs_state_changed_previous[k].add(board_migration_dict[v])

        # 标记空闲状态
        components_info.loc[dcgw_migration, "Idle"] = 0
        components_info.loc[dcgw, "Idle"] = 1

        nil = True
    else:
        nil = False
    return nil


def board_of_dcgw(dcgw, network):
    edge_info = network.edge_info
    return edge_info.loc[(edge_info.EdgeSourceNode == dcgw) & (edge_info.Type == "VL")]["EdgeDestinationNode"].to_list()


def migration_server(server, network, apps_state_changed_by_vlink, x, vnfs_state_changed_previous,
                     vlink_state_changed_previous, apps_state_changed_by_previous_vnf, fail_detect_dict_previous,
                     wait_queue_dict, print_info):
    components_info = network.components_info
    vnf_info = network.vnf_info
    vlink_info = network.vlink_info
    server_idle_list = components_info.loc[
        (components_info.Type == "Server") & (components_info.Idle == 1)].index.to_list()

    vm_list_of_origin_server = vms_of_server(server, network)
    vnfs_state_changed = {}
    apps_state_failed_by_vnf = {}
    # switch_time_dict = {}
    for vm in vm_list_of_origin_server:
        vnf = components_info.loc[vm, "VNF"]
        if vnf:
            vnf, vnf_backuptype = vnf.copy().pop().split('_')
            if vnf_backuptype == "主":
                apps_state_failed_by_vnf_vm = vnf_fail(vnf, network, vm, vnfs_state_changed_previous,
                                                       vlink_state_changed_previous,
                                                       {vm: True},
                                                       fail_detect_dict_previous,
                                                       {},
                                                       apps_state_changed_by_vlink, x,
                                                       apps_state_changed_by_previous_vnf,
                                                       wait_queue_dict,
                                                       print_info,
                                                       update_app_unavail_time=False)
                apps_state_failed_by_vnf.update(apps_state_failed_by_vnf_vm)
                # switch_time_dict[vm] = time_format(network.vnf_info.loc[vnf, "VNFFailST"])
    ts = x["EvolTime"][0]
    te = x["EvolTime"][1]

    fdf = network.fail_info
    c_type = components_info.loc[server, 'Type']
    migration_time = time_format(fdf[fdf.Type == c_type]["MST"].to_list()[0])
    migration_rate = fdf[fdf.Type == c_type]["MSR"].to_list()[0]

    update_app_state(apps_state_failed_by_vnf, apps_state_changed_by_vlink, network, ts, ts + migration_time,
                     print_info)

    if bool(server_idle_list) & (random() < migration_rate):
        server_migration = choice(server_idle_list)
        vm_list_of_migration_server = vms_of_server(server_migration, network)
        # 需要更新：1.components_info里的节点部署信息2.vnf_info里的部署信息3.vlink_info里的信息
        vm_migration_dict = dict(zip(vm_list_of_origin_server, vm_list_of_migration_server))

        apps_state_repaired_by_vnf = {}
        for vm in vm_migration_dict.keys():
            vnf_set = components_info.loc[vm, "VNF"]
            if vnf_set:
                vnf_with_backuptype = vnf_set.pop()
                components_info.loc[vm_migration_dict[vm], "VNF"].add(vnf_with_backuptype)

                vnf, backuptype = vnf_with_backuptype.split("_")
                if backuptype == "主":
                    vnf_info.loc[vnf, "VNFDeployNode"].remove(vm)
                    vnf_info.loc[vnf, "VNFDeployNode"].append(vm_migration_dict[vm])
                    state_of_vm = vnf_info.loc[vnf, "State"].pop(vm)
                    vnf_info.loc[vnf, "State"].update({vm_migration_dict[vm]: state_of_vm})
                    traffic_of_vm = vnf_info.loc[vnf, "Traffic"].pop(vm)
                    vnf_info.loc[vnf, "Traffic"].update({vm_migration_dict[vm]: traffic_of_vm})
                elif backuptype == "备":
                    vnf_info.loc[vnf, "VNFBackupNode"].remove(vm)
                    vnf_info.loc[vnf, "VNFBackupNode"].append(vm_migration_dict[vm])
                else:
                    raise (Exception('备份类型错误'))
                vnfs_state_changed[vnf] = set(vm_migration_dict[vm])

                # vlink
                vlink_of_vm_pre = vlink_info.loc[
                    (vlink_info.VlinkSourceVNF == vnf) & (vlink_info.DataType == "数据")].index.to_list()
                for vlink in vlink_of_vm_pre:
                    for u, v in list(vlink_info.loc[vlink, "State"].keys()):
                        if u == vm:
                            un = vm_migration_dict[vm]
                            vn = v
                            update_vlink_and_vl_in_component(network, u, un, v, vlink, vlink_info, vn)

                vlink_of_vm_suc = vlink_info.loc[
                    (vlink_info.VlinkDestinationVNF == vnf) & (vlink_info.DataType == "数据")].index.to_list()
                for vlink in vlink_of_vm_suc:
                    for u, v in list(vlink_info.loc[vlink, "State"].keys()):
                        if v == vm:
                            un = u
                            vn = vm_migration_dict[vm]
                            update_vlink_and_vl_in_component(network, u, un, v, vlink, vlink_info, vn)

                if print_info:
                    print("%s_%s从%s迁移到%s上" % (vnf, backuptype, vm, vm_migration_dict[vm]))

                vnf_backup_type = vnf_info.loc[vnf, "VNFBackupType"]
                # 主机型业务流量恢复
                if vnf_backup_type == "主机":
                    apps_state_repaired_by_vnf_zj = vnf_repair(vnf, network, vm_migration_dict[vm],
                                                               vnfs_state_changed_previous, {},
                                                               apps_state_changed_by_vlink, x,
                                                               apps_state_changed_by_previous_vnf,
                                                               fail_detect_dict_previous,
                                                               print_info,
                                                               update_app_unavail_time=False)
                    apps_state_repaired_by_vnf.update(apps_state_repaired_by_vnf_zj)

                # 主备型流量恢复（前提是主没有倒换）
                if (vnf_backup_type == "主备") & (backuptype == "主"):
                    apps_state_repaired_by_vnf_zb = vnf_repair(vnf, network, vm_migration_dict[vm],
                                                               vnfs_state_changed_previous, {},
                                                               apps_state_changed_by_vlink, x,
                                                               apps_state_changed_by_previous_vnf,
                                                               fail_detect_dict_previous,
                                                               print_info,
                                                               update_app_unavail_time=False)
                    apps_state_repaired_by_vnf.update(apps_state_repaired_by_vnf_zb)

                # N way型业务流量回切
                if vnf_backup_type == "N way":
                    apps_state_repaired_by_vnf_nw = vnf_repair(vnf, network, vm_migration_dict[vm],
                                                               vnfs_state_changed_previous, {},
                                                               apps_state_changed_by_vlink, x,
                                                               apps_state_changed_by_previous_vnf,
                                                               fail_detect_dict_previous,
                                                               print_info,
                                                               update_app_unavail_time=False)
                    apps_state_repaired_by_vnf.update(apps_state_repaired_by_vnf_nw)
        update_app_state(apps_state_repaired_by_vnf, apps_state_changed_by_vlink, network, ts + migration_time, te,
                         print_info)

        # 迁移完成后，更新vnfs_state_changed_previous里的信息
        for k in vnfs_state_changed_previous.keys():
            v = list(vnfs_state_changed_previous[k])[0]
            if v in vm_migration_dict.keys():
                vnfs_state_changed_previous[k].pop()
                vnfs_state_changed_previous[k].add(vm_migration_dict[v])

        # 标记空闲状态
        components_info.loc[server_migration, "Idle"] = 0
        components_info.loc[server, "Idle"] = 1

        nil = True
    else:
        nil = False
    return nil


def update_vlink_and_vl_in_component(network, u, un, v, vlink, vlink_info, vn):
    state = vlink_info.loc[vlink, "State"].pop((u, v))
    vlink_info.loc[vlink, "State"][un, vn] = state
    path_old = vlink_info.loc[vlink, "VlinkPhysicalComponents"].pop((u, v))
    path_new, nil = network.route_alorithm(un, vn, "Data", network.edge_info)
    vlink_info.loc[vlink, "VlinkPhysicalComponents"][un, vn] = path_new
    for c in path_old:
        network.components_info.loc[c, "Vlink"][vlink].remove((u, v))
        if not bool(network.components_info.loc[c, "Vlink"][vlink]):
            del network.components_info.loc[c, "Vlink"][vlink]
    for c in path_new:
        if vlink in network.components_info.loc[c, "Vlink"].keys():
            network.components_info.loc[c, "Vlink"][vlink].add((un, vn))
        else:
            network.components_info.loc[c, "Vlink"][vlink] = {(un, vn)}


def vms_of_server(server, network):
    vs = network.edge_info.loc[network.edge_info.EdgeSourceNode == server][
        'EdgeDestinationNode'].to_list()[0]
    vm_list = network.edge_info.loc[network.edge_info.EdgeSourceNode == vs]['EdgeDestinationNode'].to_list()
    return vm_list


def update_vnf_and_vlink_state_changed(component_info, component, vlinks_state_changed, vnfs_state_changed):
    vnf_in_component = component_info.loc[component, "VNF"]
    for j in vnf_in_component:
        if j.endswith("_主"):
            if j not in vnfs_state_changed:
                vnfs_state_changed[j.split("_")[0]] = {component}
            else:
                vnfs_state_changed[j.split("_")[0]].update(component)

    vlink_in_component = component_info.loc[component, "Vlink"]
    for j in vlink_in_component:
        if j not in vlinks_state_changed:
            vlinks_state_changed[j] = {component}
        else:
            vlinks_state_changed.update(component)
    return vnfs_state_changed, vlinks_state_changed


def update_graph_weight(edge_info, component, graph_plane, state):
    edge_info_indexed = edge_info.set_index("EdgeID")

    if component.startswith('Eg'):
        components_type = "edge"
    else:
        components_type = "node"
    if components_type == "edge":
        s = edge_info_indexed.loc[component, "EdgeSourceNode"]
        t = edge_info_indexed.loc[component, "EdgeDestinationNode"]

        update_edge_or_node_weight(component, edge_info, graph_plane, s, state, t)
    elif components_type == "node":
        s = component
        for t in graph_plane.neighbors(s):
            edge = edge_info.loc[((edge_info.EdgeSourceNode == s) & (edge_info.EdgeDestinationNode == t)) | (
                    (edge_info.EdgeSourceNode == t) & (edge_info.EdgeDestinationNode == s))]["EdgeID"].to_list()[0]
            update_edge_or_node_weight(edge, edge_info, graph_plane, s, state, t)
    else:
        raise (Exception("构件类型错误"))
    return graph_plane


def update_edge_or_node_weight(component, edge_info, graph_plane, s, state, t):
    edge_info = edge_info.set_index("EdgeID")
    if state == "fail":
        graph_plane.edges[(s, t)]['weight'] = float('inf')
    elif state == "reco":
        graph_plane.edges[(s, t)]['weight'] = edge_info.loc[component, "EdgeWeight"]
    else:
        raise (Exception('更新节点权值策略类型错误。'))


def update_vnf_state_by_component(vnfs_state_changed, network, fail_detect_dict, state, apps_state_changed_by_vlink, x,
                                  vnfs_state_changed_previous, vlink_state_changed_previous,
                                  apps_state_changed_by_vnf_previous, fail_detect_dict_previous,
                                  wait_queue_dict, print_info, up_app):
    apps_state_changed_by_vnf = dict()
    if state == "fail":
        if bool(vnfs_state_changed_previous) & up_app:
            ts = x["EvolTime"][0]
            te = x["EvolTime"][1]
            update_app_state(apps_state_changed_by_vnf_previous, apps_state_changed_by_vlink, network, ts, te,
                             print_info)

        for vnf in vnfs_state_changed.keys():
            apps_state_changed_by_vnf = vnf_fail(vnf, network, list(vnfs_state_changed[vnf])[0],
                                                 vnfs_state_changed_previous, vlink_state_changed_previous,
                                                 fail_detect_dict, fail_detect_dict_previous,
                                                 apps_state_changed_by_vnf,
                                                 apps_state_changed_by_vlink, x,
                                                 apps_state_changed_by_vnf_previous, wait_queue_dict, print_info)

    elif state == "repair":
        for vnf in vnfs_state_changed.keys():
            apps_state_changed_by_vnf = vnf_repair(vnf, network, list(vnfs_state_changed[vnf])[0],
                                                   vnfs_state_changed_previous,
                                                   apps_state_changed_by_vnf, apps_state_changed_by_vlink, x,
                                                   apps_state_changed_by_vnf_previous, fail_detect_dict_previous,
                                                   print_info)
    else:
        raise Exception("输入VNF状态错误")
    return apps_state_changed_by_vnf


def update_vlink_state_changed_by_component(vlinks_state_changed, network, fail_detect_dict, state, x,
                                            vlinks_state_changed_previous, apps_state_changed_by_previous_vlink,
                                            print_info):
    apps_state_changed_by_vlink = dict()
    ts = x['EvolTime'][0]
    te = x['EvolTime'][1]
    if vlinks_state_changed_previous:
        vlinks_state_changed_previous_data = {i: vlinks_state_changed_previous[i] for i in
                                              vlinks_state_changed_previous.keys() if
                                              network.vlink_info.loc[i, "DataType"] == "数据"}
        vlinks_state_changed.update(vlinks_state_changed_previous_data)
    if state == "fail":
        for vlink in vlinks_state_changed.keys():
            apps_state_changed_by_vlink = vlink_fail(vlink, network, vlinks_state_changed[vlink].copy().pop(),
                                                     vlinks_state_changed_previous, fail_detect_dict,
                                                     apps_state_changed_by_vlink, apps_state_changed_by_previous_vlink,
                                                     print_info)
        update_app_state({}, apps_state_changed_by_vlink, network, ts, te, print_info)
    elif state == "repair":
        for vlink in vlinks_state_changed.keys():
            apps_state_changed_by_vlink = vlink_repair(vlink, network, vlinks_state_changed[vlink].copy().pop(),
                                                       vlinks_state_changed_previous,
                                                       apps_state_changed_by_vlink,
                                                       apps_state_changed_by_previous_vlink,
                                                       print_info)
        update_app_state({}, apps_state_changed_by_vlink, network, ts, te, print_info)
    else:
        raise Exception("输入Vlink状态错误")
    return apps_state_changed_by_vlink


def can_switch(vnf, network, vlink_state_changed_previous):
    if network.control_plane_type == ControlPlaneType.NONE:
        return True, []

    vnf_info = network.vnf_info
    vlink_info = network.vlink_info
    if vnf_info.loc[vnf, "VNFDataType"] != "数据":
        return True, []

    app_undirected_graph = network.application_graph.to_undirected()
    if vnf in app_undirected_graph.nodes:
        vnf_neighbors_iter = app_undirected_graph.neighbors(vnf)
        vnf_neighbors_df = vnf_info.loc[vnf_neighbors_iter]
    else:
        vnf_neighbors_iter = None
        vnf_neighbors_df = None

    nce_df = vnf_info.loc[vnf_info.VNFDataType == "NCE"]
    dcgw_df = vnf_info.loc[vnf_info.VNFDataType == "DCGW控制"]

    if not nce_df.State.apply(key_all_one).all():
        control_component_fail_list = find_fail_control_node(nce_df)
        return False, control_component_fail_list
    if not dcgw_df.State.apply(key_all_one).all():
        control_component_fail_list = find_fail_control_node(dcgw_df)
        return False, control_component_fail_list

    for vnf_nce in nce_df.index:
        vl_vnf_nce_df = vlink_info.loc[(vlink_info.VlinkSourceVNF == vnf) & (vlink_info.VlinkDestinationVNF == vnf_nce)]
        if not vl_vnf_nce_df.State.apply(key_all_one).all():
            control_component_fail_list = find_fail_control_edge(vl_vnf_nce_df, vlink_state_changed_previous)
            return False, control_component_fail_list

    if vnf_neighbors_iter:
        if not vnf_neighbors_df.State.apply(key_all_one).all():
            control_component_fail_list = find_fail_control_node(vnf_neighbors_df)
            return False, control_component_fail_list
        vl_data_df = vlink_info.loc[vlink_info.DataType == '数据']
        vl_neighbors_df = vl_data_df.loc[
            ((vl_data_df.VlinkSourceVNF == vnf) & (vl_data_df.VlinkDestinationVNF in vnf_neighbors_iter)) | (
                    (vl_data_df.VlinkSourceVNF in vnf_neighbors_iter) & (vl_data_df.VlinkDestinationVNF == vnf))]
        if not vl_neighbors_df.State.apply(key_all_one).all():
            control_component_fail_list = find_fail_control_edge(vl_neighbors_df, vlink_state_changed_previous)
            return False, control_component_fail_list

    if network.control_plane_type == ControlPlaneType.CENTRALIZATION:
        vl_nce_dcgw_df = vlink_info.loc[
            (vlink_info.VlinkSourceVNF.isin(nce_df.index)) & (vlink_info.VlinkDestinationVNF.isin(dcgw_df.index))]
        if not vl_nce_dcgw_df.State.apply(key_all_one).all():
            control_component_fail_list = find_fail_control_edge(vl_nce_dcgw_df, vlink_state_changed_previous)
            return False, control_component_fail_list
    elif network.control_plane_type == ControlPlaneType.DISTRIBUTION:
        if vnf_neighbors_iter:
            vl_neighbors_dcgw_df = vlink_info.loc[
                (vlink_info.VlinkSourceVNF in vnf_neighbors_iter) & (
                    vlink_info.VlinkDestinationVNF.isin(dcgw_df.index))]
            if not vl_neighbors_dcgw_df.State.apply(key_all_one).all():
                control_component_fail_list = find_fail_control_edge(vl_neighbors_dcgw_df, vlink_state_changed_previous)
                return False, control_component_fail_list
    else:
        raise (Exception('控制平面类型错误'))
    return True, []


def find_fail_control_node(df):
    control_component_fail_list_of_list = df.State.apply(lambda x: [i for i in x.keys() if x[i] == 0]).to_list()
    control_component_fail_list = functools.reduce(lambda x, y: set(x).union(y), control_component_fail_list_of_list)
    return control_component_fail_list


def find_fail_control_edge(df, vlink_state_changed_previous):
    fail_control_edge_set = set()
    fail_vl_list = df.index.to_list()
    for fail_vl in fail_vl_list:
        if fail_vl in vlink_state_changed_previous.keys():
            fail_control_edge_set = fail_control_edge_set.union(vlink_state_changed_previous[fail_vl])
    return list(fail_control_edge_set)


def vnf_fail(vnf, network, component, vnfs_state_changed_previous, vlink_state_changed_previous,
             fail_detect_dict, fail_detect_dict_previous, apps_state_changed_by_vnf, apps_state_changed_by_vlink, x,
             apps_state_changed_by_previous_vnf, wait_queue_dict, print_info,
             update_app_unavail_time=True):
    fdf = network.fail_info
    apps_state_changed_by_vnf.update({i: vnf for i in network.vnf_info.loc[vnf, "ApplicationDeployed"]})
    if apps_state_changed_by_previous_vnf:
        apps_state_changed_by_vnf.update(apps_state_changed_by_previous_vnf)
        fail_detect_dict[component] = True

    if fail_detect_dict_previous:
        fail_detect_dict.update(fail_detect_dict_previous)

    ts = x["EvolTime"][0]
    te = x["EvolTime"][1]
    vnf_backup_type = network.vnf_info.loc[vnf, "VNFBackupType"]
    if vnf_backup_type == "主机":
        network.vnf_info.loc[vnf, "State"][component] = 0
        network.vnf_info.loc[vnf, "Traffic"][component] = 0.0
        if print_info:
            print("%s故障,流量变为为%s" % (vnf, network.vnf_info.loc[vnf, "Traffic"][component]))
        if update_app_unavail_time:
            update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network, ts, te, print_info)
        vnfs_state_changed_previous.update({vnf: {component}})
        apps_state_changed_by_previous_vnf.update({i: vnf for i in network.vnf_info.loc[vnf, "ApplicationDeployed"]})
    elif vnf_backup_type == "主备":
        master = network.vnf_info.loc[vnf, "VNFDeployNode"][0]
        slave = network.vnf_info.loc[vnf, "VNFBackupNode"][0]
        network.vnf_info.loc[vnf, "State"][master] = 0
        network.vnf_info.loc[vnf, "Traffic"][master] = 0

        c_type = network.components_info.loc[component, "Type"]
        switch_time = time_format(fdf[fdf.Type == c_type]["MST"].to_list()[0])
        switch_rate = fdf[fdf.Type == c_type]["MSR"].to_list()[0]

        if fail_detect_dict[component]:
            can_switch_nil, fail_control_component_list = can_switch(vnf, network, vlink_state_changed_previous)
            if can_switch_nil:
                # 需要判断备的状态是否正常
                if random() < switch_rate:
                    if network.components_info.loc[slave, "State"] == 1:
                        if update_app_unavail_time:
                            update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network, ts,
                                             ts + switch_time, print_info)
                        # 倒换, 两个操作，1.vnf_info里变换，2. components_info里面变换
                        network.vnf_info.loc[vnf, "VNFDeployNode"].clear()
                        network.vnf_info.loc[vnf, "VNFDeployNode"].append(slave)
                        network.vnf_info.loc[vnf, "VNFBackupNode"].clear()
                        network.vnf_info.loc[vnf, "VNFBackupNode"].append(master)
                        network.vnf_info.loc[vnf, "State"].pop(master)
                        network.vnf_info.loc[vnf, "State"][slave] = 1
                        network.vnf_info.loc[vnf, "Traffic"].pop(master)
                        network.vnf_info.loc[vnf, "Traffic"][slave] = 1
                        network.components_info.loc[master, "VNF"].clear()
                        network.components_info.loc[master, "VNF"].add("%s_备" % vnf)
                        network.components_info.loc[slave, "VNF"].clear()
                        network.components_info.loc[slave, "VNF"].add("%s_主" % vnf)
                        if print_info:
                            print("%s主备倒换，主节点%s倒换至备用节点%s上，流量为%s" % (
                                vnf, master, slave, network.vnf_info.loc[vnf, "Traffic"][slave]))
                        # 在倒换后，在vlink中更新链路信息
                        vlink_update_list = network.vlink_info.loc[
                            ((network.vlink_info.VlinkSourceVNF == vnf) | (
                                    network.vlink_info.VlinkDestinationVNF == vnf)) & (
                                    network.vlink_info.DataType == "数据")].index.to_list()
                        for vlink in vlink_update_list:
                            key_list = list(network.vlink_info.loc[vlink, "State"].keys())
                            for u, v in key_list:
                                if u == master:
                                    un, vn = slave, v
                                elif v == master:
                                    un, vn = u, slave
                                else:
                                    raise (Exception("主备更换vlink时出现错误"))
                                update_vlink_and_vl_in_component(network, u, un, v, vlink, network.vlink_info, vn)
                        if update_app_unavail_time:
                            update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network,
                                             ts + switch_time, te, print_info)

                    else:
                        if print_info:
                            print("%s的备用节点故障,无法倒换,流量变为%s" % (vnf, network.vnf_info.loc[vnf, "Traffic"][master]))
                        if update_app_unavail_time:
                            update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network, ts, te,
                                             print_info)
                        vnfs_state_changed_previous.update({vnf: {component}})
                        apps_state_changed_by_previous_vnf.update(
                            {i: vnf for i in network.vnf_info.loc[vnf, "ApplicationDeployed"]})
                        fail_detect_dict_previous[component] = True
                else:
                    if print_info:
                        print("%s由于倒换概率问题,未发生倒换,流量变为%s" % (vnf, network.vnf_info.loc[vnf, "Traffic"][master]))
                    if update_app_unavail_time:
                        update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network, ts, te,
                                         print_info)
                    vnfs_state_changed_previous.update({vnf: {component}})
                    apps_state_changed_by_previous_vnf.update(
                        {i: vnf for i in network.vnf_info.loc[vnf, "ApplicationDeployed"]})
                    fail_detect_dict_previous[component] = True
            else:
                for control_component in fail_control_component_list:
                    network.vnf_info.loc[vnf, "Wait"].add(control_component)
                    wait_queue_add(control_component, vnf, wait_queue_dict)
                if print_info:
                    print("%s故障,处于等待倒换状态" % vnf)
                if update_app_unavail_time:
                    update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network, ts, te,
                                     print_info)
                vnfs_state_changed_previous.update({vnf: {component}})
                apps_state_changed_by_previous_vnf.update(
                    {i: vnf for i in network.vnf_info.loc[vnf, "ApplicationDeployed"]})
                fail_detect_dict_previous[component] = True
        else:
            if print_info:
                print("%s故障未检测,不进行倒换" % vnf)
            if update_app_unavail_time:
                update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network, ts, te, print_info)
            vnfs_state_changed_previous.update({vnf: {component}})
            apps_state_changed_by_previous_vnf.update(
                {i: vnf for i in network.vnf_info.loc[vnf, "ApplicationDeployed"]})
            fail_detect_dict_previous[component] = False

    elif vnf_backup_type == "N way":
        c_type = network.components_info.loc[component, "Type"]
        switch_time = time_format(fdf[fdf.Type == c_type]["MST"].to_list()[0])
        switch_rate = fdf[fdf.Type == c_type]["MSR"].to_list()[0]

        if fail_detect_dict[component]:
            can_switch_nil, fail_control_component_list = can_switch(vnf, network, vlink_state_changed_previous)
            if can_switch_nil:
                if random() < switch_rate:
                    # 两个操作：1.vnf_info里分担流量 2.compontents_info里更新
                    state = network.vnf_info.loc[vnf, "State"]
                    slaves = {i for i in state.keys() if (state[i] == 1) & (i != component)}
                    component_traffic = network.vnf_info.loc[vnf, "Traffic"][component]

                    network.vnf_info.loc[vnf, "State"][component] = 0
                    network.vnf_info.loc[vnf, "Traffic"][component] = 0
                    if update_app_unavail_time:
                        update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network, ts,
                                         ts + switch_time, print_info)

                    for i in slaves:
                        network.vnf_info.loc[vnf, "Traffic"][i] += component_traffic / len(slaves)
                    if print_info:
                        print("%s的N way倒换，分支%s的流量被分担到%s中,流量变为%s" % (
                            vnf, component, slaves, sum(network.vnf_info.loc[vnf, "Traffic"].values())))
                    if update_app_unavail_time:
                        update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network,
                                         ts + switch_time, te, print_info)
                    if sum(network.vnf_info.loc[vnf, "Traffic"].values()) < 1:
                        vnfs_state_changed_previous.update({vnf: {component}})
                        apps_state_changed_by_previous_vnf.update(
                            {i: vnf for i in network.vnf_info.loc[vnf, "ApplicationDeployed"]})
                        fail_detect_dict_previous[component] = True
                else:
                    network.vnf_info.loc[vnf, "State"][component] = 0
                    network.vnf_info.loc[vnf, "Traffic"][component] = 0
                    if print_info:
                        print("%s由于倒换概率问题,未发生倒换,流量变为%s" % (vnf, sum(network.vnf_info.loc[vnf, "Traffic"].values())))
                    if update_app_unavail_time:
                        update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network, ts, te,
                                         print_info)
                    vnfs_state_changed_previous.update({vnf: {component}})
                    apps_state_changed_by_previous_vnf.update(
                        {i: vnf for i in network.vnf_info.loc[vnf, "ApplicationDeployed"]})
                    fail_detect_dict_previous[component] = True
            else:
                for control_component in fail_control_component_list:
                    network.vnf_info.loc[vnf, "Wait"].add(control_component)
                    wait_queue_add(control_component, vnf, wait_queue_dict)
                network.vnf_info.loc[vnf, "State"][component] = 0
                network.vnf_info.loc[vnf, "Traffic"][component] = 0
                if print_info:
                    print("%s故障,处于等待倒换状态" % vnf)
                if update_app_unavail_time:
                    update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network, ts, te,
                                     print_info)
                vnfs_state_changed_previous.update({vnf: {component}})
                apps_state_changed_by_previous_vnf.update(
                    {i: vnf for i in network.vnf_info.loc[vnf, "ApplicationDeployed"]})
                fail_detect_dict_previous[component] = True
        else:
            network.vnf_info.loc[vnf, "State"][component] = 0
            network.vnf_info.loc[vnf, "Traffic"][component] = 0
            if print_info:
                print("%s由于故障未检测，不进行倒换,流量变为%s" % (vnf, sum(network.vnf_info.loc[vnf, "Traffic"].values())))
            if update_app_unavail_time:
                update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network, ts, te,
                                 print_info)
            vnfs_state_changed_previous.update({vnf: {component}})
            apps_state_changed_by_previous_vnf.update(
                {i: vnf for i in network.vnf_info.loc[vnf, "ApplicationDeployed"]})
            fail_detect_dict_previous[component] = False
    else:
        raise (Exception("VNF备份类型未定义"))
    return apps_state_changed_by_vnf


def wait_queue_add(component, vnf, wait_queue_dict):
    if component not in wait_queue_dict.keys():
        wait_queue_dict[component] = {vnf}
    else:
        wait_queue_dict[component].add(vnf)


def vnf_repair(vnf, network, component, vnfs_state_changed_previous, apps_state_changed_by_vnf,
               apps_state_changed_by_vlink, x, apps_state_changed_by_previous_vnf, fail_detect_dict_previous,
               print_info, update_app_unavail_time=True):
    apps_state_changed_by_vnf.update({i: vnf for i in network.vnf_info.loc[vnf, "ApplicationDeployed"]})
    if vnf in vnfs_state_changed_previous.keys():
        del vnfs_state_changed_previous[vnf]
    for k in list(apps_state_changed_by_previous_vnf.keys()):
        if apps_state_changed_by_previous_vnf[k] == vnf:
            del apps_state_changed_by_previous_vnf[k]
    if component in fail_detect_dict_previous.keys():
        del fail_detect_dict_previous[component]
    ts = x["EvolTime"][0]
    te = x["EvolTime"][1]

    vnf_backup_type = network.vnf_info.loc[vnf, "VNFBackupType"]
    if vnf_backup_type == "主机":
        network.vnf_info.loc[vnf, "State"][component] = 1
        network.vnf_info.loc[vnf, "Traffic"][component] = 1.0
        if print_info:
            print("%s修复,流量变为%s" % (vnf, network.vnf_info.loc[vnf, "Traffic"][component]))
        if update_app_unavail_time:
            update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network, ts, te, print_info)
    elif vnf_backup_type == "主备":
        master = network.vnf_info.loc[vnf, "VNFDeployNode"][0]
        network.vnf_info.loc[vnf, "State"][master] = 1
        network.vnf_info.loc[vnf, "Traffic"][master] = 1.0
        if print_info:
            print("%s修复,流量变为%s" % (vnf, network.vnf_info.loc[vnf, "Traffic"][master]))
        if update_app_unavail_time:
            update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network, ts, te, print_info)
    elif vnf_backup_type == "N way":
        state_dict = network.vnf_info.loc[vnf, "State"]
        traffic_dict = network.vnf_info.loc[vnf, "Traffic"]
        slaves = {i for i in state_dict.keys() if (state_dict[i] == 1) & (i != component)}
        network.vnf_info.loc[vnf, "State"][component] = 1

        vm_ok_list = [i for i in state_dict.keys() if state_dict[i] == 1]
        for i in traffic_dict:
            traffic_dict[i] = 0
        for i in vm_ok_list:
            traffic_dict[i] = 1 / len(vm_ok_list)

        if print_info:
            print("%s的N way倒换，分支%s的流量从%s中回切，流量变为%s" % (
                vnf, component, slaves, sum(network.vnf_info.loc[vnf, "Traffic"].values())))

        if update_app_unavail_time:
            update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network, ts, te, print_info)
    else:
        raise (Exception("VNF备份类型未定义"))
    return apps_state_changed_by_vnf


def vlink_fail(vlink, network, component, vlinks_state_changed_previous, fail_detect_dict, apps_state_changed_by_vlink,
               apps_state_changed_by_previous_vlink, print_info):
    apps_state_changed_by_vlink.update({i: vlink for i in network.vlink_info.loc[vlink, "ApplicationDeployed"]})
    if apps_state_changed_by_previous_vlink:
        apps_state_changed_by_vlink.update(apps_state_changed_by_previous_vlink)
        fail_detect_dict[component] = True
    vlink_type = network.vlink_info.loc[vlink, "DataType"]
    if fail_detect_dict[component]:
        vl_list = list(network.components_info.loc[component, "Vlink"][vlink])
        for ns, nt in vl_list:
            if (ns, nt) in network.vlink_info.loc[vlink, "VlinkPhysicalComponents"].keys():
                path_old = network.vlink_info.loc[vlink, "VlinkPhysicalComponents"][(ns, nt)]
                path_new, nil = network.route_alorithm(ns, nt, vlink_type, network.edge_info)
                if nil:
                    network.vlink_info.loc[vlink, "VlinkPhysicalComponents"][(ns, nt)] = path_new
                    network.update_vlink_of_component(ns, nt, path_new, path_old, vlink)
                    if vlink_type == "数据":
                        if print_info:
                            print("由于%s的故障，%s上的节点%s到%s的链路分支更换至%s" % (component,
                                                                     vlink, ns, nt, path_new))
                    else:
                        if print_info:
                            print("由于%s的故障，控制链路%s上的节点%s到%s的链路分支更换至%s" % (component,
                                                                         vlink, ns, nt, path_new))
                else:
                    network.vlink_info.loc[vlink, "State"][ns, nt] = 0
                    if vlink_type == "数据":
                        if print_info:
                            print("由于%s的故障，%s上的%s分支未找到合适的替换链路，流量中断" % (component,
                                                                       vlink, (ns, nt)))
                    else:
                        if print_info:
                            print("由于%s的故障，控制链路%s故障" % (component, vlink))
                    vlinks_state_changed_previous.update({vlink: {component}})
                    apps_state_changed_by_previous_vlink.update(
                        {i: vlink for i in network.vlink_info.loc[vlink, "ApplicationDeployed"]})

    else:
        for ns, nt in list(network.components_info.loc[component, "Vlink"][vlink]):
            if (ns, nt) in network.vlink_info.loc[vlink, "VlinkPhysicalComponents"].keys():
                network.vlink_info.loc[vlink, "State"][ns, nt] = 0
                if vlink_type == "数据":
                    if print_info:
                        print("由于%s的故障未被检测，%s上的%s分支未找到合适的替换链路，流量中断" % (component,
                                                                       vlink, (ns, nt)))
                else:
                    if print_info:
                        print("由于%s的故障未被检测，控制链路%s故障" % (component, vlink))
                vlinks_state_changed_previous.update({vlink: {component}})
                apps_state_changed_by_previous_vlink.update(
                    {i: vlink for i in network.vlink_info.loc[vlink, "ApplicationDeployed"]})
    return apps_state_changed_by_vlink


def vlink_repair(vlink, network, component, vlinks_state_changed_previous, apps_state_changed_by_vlink,
                 apps_state_changed_by_previous_vlink, print_info):
    apps_state_changed_by_vlink.update({i: vlink for i in network.vlink_info.loc[vlink, "ApplicationDeployed"]})
    if vlink in vlinks_state_changed_previous.keys():
        del vlinks_state_changed_previous[vlink]
    for k in list(apps_state_changed_by_previous_vlink.keys()):
        if apps_state_changed_by_previous_vlink[k] == vlink:
            del apps_state_changed_by_previous_vlink[k]
    vlink_type = network.vlink_info.loc[vlink, "DataType"]
    for ns, nt in network.vlink_info.loc[vlink, "VlinkPhysicalComponents"].keys():
        if {component}.intersection(network.vlink_info.loc[vlink, "VlinkPhysicalComponents"][(ns, nt)]):
            network.vlink_info.loc[vlink, "State"][ns, nt] = 1
            if vlink_type == "数据":
                if print_info:
                    print("由于%s的修复,%s上的%s分支流量恢复" % (component, vlink, (ns, nt)))
            else:
                if print_info:
                    print("由于%s的修复，控制链路%s修复" % (component, vlink))
    return apps_state_changed_by_vlink


def key_all_one(x: dict):
    for i in x.values():
        if i != 1:
            return False
    return True


def update_app_state(apps_state_changed_by_vnf, apps_state_changed_by_vlink, network, time_start, time_stop,
                     print_info):
    t = time_stop - time_start
    # 计算业务流量和业务不可用时间

    apps = set(apps_state_changed_by_vnf.keys()).union(set(apps_state_changed_by_vlink.keys()))
    if apps:
        for app in apps:
            vnf_list = network.application_info.loc[app, "ApplicationVNFs"]
            app_traffic = 1.0
            vnf_pairs_calculated_set = set()
            for i in range(len(vnf_list) - 1):
                vnf_pre = vnf_list[i]
                vnf_suc = vnf_list[i + 1]
                if (vnf_pre, vnf_suc) not in vnf_pairs_calculated_set:
                    vnf_pre_traffic_dict = network.vnf_info.loc[vnf_pre, "Traffic"]
                    vnf_suc_traffic_dict = network.vnf_info.loc[vnf_suc, "Traffic"]
                    vlink_series = network.vlink_info.loc[(network.vlink_info.VlinkSourceVNF == vnf_pre) & (
                            network.vlink_info.VlinkDestinationVNF == vnf_suc)].iloc[0]
                    vlink_state_dict = vlink_series["State"]
                    vnf_pair_traffic = 0.0
                    for component_vnf_pre in vnf_pre_traffic_dict.keys():
                        for component_vnf_suc in vnf_suc_traffic_dict.keys():
                            vnf_pair_traffic += vnf_pre_traffic_dict[component_vnf_pre] * vnf_suc_traffic_dict[
                                component_vnf_suc] * vlink_state_dict[component_vnf_pre, component_vnf_suc]
                    app_traffic *= vnf_pair_traffic

                    vnf_pairs_calculated_set.add((vnf_pre, vnf_suc))
                    vnf_pairs_calculated_set.add((vnf_suc, vnf_pre))
            if print_info:
                print("业务%s的流量在时间[%s, %s]小时是%s" % (app, time_start, time_stop, app_traffic))

            network.application_info.loc[app, "Traffic"] = app_traffic
            weighted_unavail_time = network.application_info.loc[app, "WeightedUnavailTime"]
            if app_traffic < network.application_info.loc[app, "TrafficThreshold"]:
                network.application_info.loc[app, "WeightedUnavailTime"] = weighted_unavail_time + (1.0 - app_traffic) \
                                                                           * t
